package dst.ass1.nosql.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.MapReduceCommand;
import com.mongodb.MapReduceOutput;
import dst.ass1.nosql.IMongoDbQuery;

public class MongoDbQuery implements IMongoDbQuery {

    private final DBCollection collection;

    private final static Logger LOGGER = Logger.getLogger(MongoDbQuery.class.getName());
    
    public MongoDbQuery(DB db) {
        collection = db.getCollection(MongoDbDataLoader.COLLECTION_NAME);
    }

    /**
     * 1.5.b. (2)
     * fetch all results with a last updated field later
	 * than the given timestamp. For the second query, use a filter to retrieve only the actual task id of
	 * the document (no other properties should be contained in the delivered result)
     */
    @Override
    public List<Long> findLastUpdatedGt(Long time) {
        BasicDBObject query = new BasicDBObject("last_updated",new BasicDBObject("$gt", time));  //greater than the given time as long  --> http://docs.mongodb.org/ecosystem/tutorial/getting-started-with-java-driver/
        BasicDBObject filter = new BasicDBObject("task_id", 1);  //defined filter for object i want to get back

        DBCursor cursor = collection.find(query, filter);  //apply filter for task id

        List<Long> l = new ArrayList<Long>();

        while (cursor.hasNext()) {
            DBObject object = cursor.next();
            Long taskId = (Long)object.get("task_id");  //TaskId from object
            l.add(taskId.longValue());  //add in return list

            LOGGER.info("findLastUpdatedTask with Id: "+taskId);
        }

        return l;
    }

    /**
     * 1.5.c
     * Map/Reduce
     * map/reduce via JavaScript both the map and the reduce functions as JavaScript functions are needed that operate on 
     * the stored documents
     * 
     * The output of the map/reduce query is a list of all existing (top-level) payload document
	 * properties, along with the number of documents that they occur in
     */
    @Override
    public List<DBObject> mapReduceWorkflow() {
        String map =
                "function() { " +
                " for (var prop in this) { " +
                " switch (prop) { " +
                " case \"_id\": " +				//ignore this property to count
                " case \"task_id\": " +			//ignore this property to count
                " case \"last_updated\": " +	//ignore this property to count
                " continue; " +
                " default: " +
                " emit (prop, 1); " +			//this property should be aggregated (should be logs, result_matrix, alignment_block) according to the test cases
                " } " +
                " }" +
                "}";
        String reduce = "function(key, values) { " +   //sum up all values (1) for a key
                " var sum = 0; " +
                " for (var v in values) { " +
                " sum += values[v]; " +				//the value is 1 because of emit(prop,1) in map function
                " } " +
                " return sum; " +
                "}";

        MapReduceOutput out = collection.mapReduce(map, reduce, null,MapReduceCommand.OutputType.INLINE, null);

        List<DBObject> l = new ArrayList<DBObject>();
        for (DBObject object : out.results()) {
        	Integer taskId = (Integer)object.get("task_id");
            l.add(object);
            LOGGER.info("mapReduce Oject with Task Id: "+taskId);
        }

        return l;
    }

    
    /**
     * 1.5.b. (1)
     * fetch the result document for a given task id (e.g., for the task id 1)
	 * and return the timestamp of the last update.
     */
	@Override
	public Long findLastUpdatedByTaskId(Long id) {
		BasicDBObject query = new BasicDBObject("task_id", id);
		
		DBObject object = collection.findOne(query);

        if (object==null) {
        	LOGGER.info("findLastUpdatedByTaskId: No matching object found for task_id: "+id);
            return new Long(-1);
        }

        Long lastUpdated = (Long)object.get("last_updated");

        LOGGER.info(String.format("findLastUpdatedByTaskId: Found last_updated: %d for task_id: %d%n",lastUpdated.longValue(), id));

        return lastUpdated.longValue();
	}

}
